package com.dferreira.repository.data_delegators;

import android.content.Context;
import android.content.res.AssetManager;
import android.util.Log;

import com.dferreira.languagesteach.utils.DataPaths;
import com.dferreira.languagesteach.utils.DefaultLanguagesData;
import com.dferreira.repository.RepositoryDatabaseHelper;
import com.dferreira.repository.data_models.PackageCommon;
import com.dferreira.repository.data_models.PackageMetadata;
import com.dferreira.repository.data_models.PackageMetadataState;
import com.dferreira.repository.data_models.Repository;
import com.dferreira.repository.data_utils.FileEntryZIPExtractor;
import com.j256.ormlite.dao.RuntimeExceptionDao;
import com.j256.ormlite.stmt.QueryBuilder;
import com.j256.ormlite.stmt.Where;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class DefaultDataSource {
    private static final String TAG = "DefaultDataSource";

    private final Context context;
    private final FileEntryZIPExtractor zipPackageUtils;
    private final DefaultLanguagesData defaultLanguagesData;
    private final DataPaths dataPaths;
    private RepositoryDatabaseHelper repositoryDbHelper;


    /**
     * Constructor
     *
     * @param context global information about an application environment.
     */
    public DefaultDataSource(Context context) {
        this.context = context;
        this.dataPaths = new DataPaths(context);
        this.zipPackageUtils = new FileEntryZIPExtractor(context);
        this.repositoryDbHelper = new RepositoryDatabaseHelper(context);

        if (this.zipPackageUtils.setPackageFolder(dataPaths.getPackagesPath()) == null) {
            this.defaultLanguagesData = new DefaultLanguagesData(context);
        } else {
            defaultLanguagesData = null;
        }
    }

    /**
     * @return false -> Without default package installed true -> With default
     * package installed
     */
    private boolean verifyIfAlreadyHaveDefaultPackage() {
        String packageName;

        packageName = defaultLanguagesData.getDefaultPackagesMetadata()[0]
                .getName();
        PackageMetadata defaultPackage = getPackageMetadataByName(packageName);
        return (defaultPackage != null);
    }


    /**
     * @param installAnyWay   install the package even if he already exists
     * @param packageMetadata information about the package to install
     */
    private void installADefaultPackage(boolean installAnyWay, PackageMetadata packageMetadata) {

        InputStream inputStream;
        AssetManager assetManager;
        Repository defaultRepository;
        /* Cursors */

        PackageMetadata defaultPackage = getPackageMetadataByName(packageMetadata.getName());
        if ((defaultPackage != null) && (!installAnyWay)) {
            return;
        }

        if (!installAnyWay) {
            defaultRepository = defaultLanguagesData.getDefaultRepository();
            packageMetadata.setRepository(defaultRepository);
            packageMetadata.setState(PackageMetadataState.Created);
            createPackageMetadata(packageMetadata);
        }
        packageMetadata.setState(PackageMetadataState.InstallingFiles);
        updatePackageMetadata(packageMetadata);

        assetManager = context.getAssets();
        try {
            inputStream = assetManager.open("data/"
                    + packageMetadata.getName() + ".zip");
            zipPackageUtils.beginInstallingPackage(inputStream);
            while (zipPackageUtils.fetchNextEntry()) {
                long entrySize = zipPackageUtils.installAnEntry(packageMetadata);
                if (entrySize < 0) {
                    return;
                }
            }

            packageMetadata.setState(PackageMetadataState.Installed);
            updatePackageMetadata(packageMetadata);

        } catch (IOException e) {
            Log.w(TAG, e);
            deletePackageMetadata(packageMetadata.getId());
        }
    }

    /**
     * @param packageId the id from the package to delete
     */
    private void deletePackageMetadata(Integer packageId) {
        try {
            RuntimeExceptionDao<PackageMetadata, Integer> dao = repositoryDbHelper.getPackageMetadataDao();

            QueryBuilder<PackageMetadata, Integer> builder = dao.queryBuilder();
            Where<PackageMetadata, Integer> where = builder.where();
            where.eq(PackageCommon.COLUMN_NAME_ID, packageId);
            List<PackageMetadata> list = dao.query(builder.prepare());
            dao.delete(list);
        } catch (java.sql.SQLException e) {
            Log.w(TAG, e);
        }
    }

    /**
     * Put the information about the default package the database
     *
     * @param installAnyWay when is true will install the default package even if is already installed
     */
    public void install(boolean installAnyWay) {
        Repository defaultRepository;
        PackageMetadata[] defaultPackages;

        if (!installAnyWay && verifyIfAlreadyHaveDefaultPackage()) {
            return;
        }

        defaultPackages = defaultLanguagesData.getDefaultPackagesMetadata();


        defaultRepository = defaultLanguagesData.getDefaultRepository();

        deleteAllRepositories();
        createRepository(defaultRepository);

        for (PackageMetadata defaultPackage : defaultPackages) {
            installADefaultPackage(installAnyWay, defaultPackage);
        }
    }

    /**
     * method for insert a repository
     *
     * @param repository
     * @return
     */
    private int createRepository(Repository repository) {
        RuntimeExceptionDao<Repository, Integer> dao = repositoryDbHelper.getRepositoryDao();
        int i = dao.create(repository);
        return i;
    }

    /**
     * @param packageMetadata
     * @return
     */
    private int createPackageMetadata(PackageMetadata packageMetadata) {
        RuntimeExceptionDao<PackageMetadata, Integer> dao = repositoryDbHelper.getPackageMetadataDao();
        return dao.create(packageMetadata);
    }

    /**
     * @param packageMetadata
     * @return
     */
    private int updatePackageMetadata(PackageMetadata packageMetadata) {
        RuntimeExceptionDao<PackageMetadata, Integer> dao = repositoryDbHelper.getPackageMetadataDao();
        return dao.update(packageMetadata);
    }

    /**
     * method for delete all rows
     */
    private void deleteAllRepositories() {
        RuntimeExceptionDao<Repository, Integer> dao = repositoryDbHelper.getRepositoryDao();
        List<Repository> list = dao.queryForAll();
        dao.delete(list);
    }

    /**
     * @param name
     * @return
     */
    private PackageMetadata getPackageMetadataByName(String name) {
        try {
            RuntimeExceptionDao<PackageMetadata, Integer> dao = repositoryDbHelper.getPackageMetadataDao();
            QueryBuilder<PackageMetadata, Integer> builder = dao.queryBuilder();
            Where<PackageMetadata, Integer> where = builder.where();
            where.eq(PackageMetadata.COLUMN_NAME_NAME, name);
            builder.limit(1l);

            return dao.queryForFirst(builder.prepare());
        } catch (java.sql.SQLException e) {
            Log.w(TAG, e);
            return null;
        }
    }
}
